#!/usr/bin/env python
# -*- mode: python; coding: utf-8; -*-
##--------------------------------------------------------------------------##
##
## Copyright (C) 1998-2003 Markus Oberhumer <markus@oberhumer.com>
## Copyright (C) 2003 Mt. Hood Playing Card Co.
## Copyright (C) 2005-2010 Skomoroh <skomoroh@users.sourceforge.net>
## Copyright (C) 2010-2013 Jeremy Austin-Bardo <tjaustinbardo@gmail.com>
##
## This program is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program.  If not, see <http://www.gnu.org/licenses/>.
##
##-- G22 -- S07 ------------------------------------------------------------##
__all__ = []

from .. import formlib as layout
from .. import pilelib as stack
from .. import settings as c
from .. import stackfill as fill
from ..forms import (klondike as KlondikeLayout, gypsy as GypsyLayout,
                     freecell as FreeCellLayout,)
from ..gamelib import info as gi, state, game
from ..hint import CautiousDefaultHint
from ..packlib import info as cs, place
from ..pilelib import move as a_move
from ..utillib.misc import kwdefault

GT, SL = gi.GT, gi.SL
Wrap = stack.StackWrapper


class ShuffleRedealTalon(stack.RedealTalon):

    def deal_cards(self, **kw):
        """
        deal_cards([sound:bool=False, shuffle=True, reverse=False]) -> int

        Actual dealing, called from game.talon_deal(). Overide parent, to
        allow cards to be shuffled before being redealt.

        :keyword boolean sound: sound fx?
        :keyword boolean shuffle: shuffle cards before redealing?
        :keyword boolean reverse: deals cards in reverse order?
        :return: number of cards dealt.
        """
        kw.setdefault('shuffle', True)
        super(ShuffleRedealTalon, self).redeal_cards()


class Crescent_Talon(stack.RedealTalon):

    @property
    def _piles(self):
        """
        _piles -> tup

        :return: deal Game.s.rows
        :rtype: tuple
        """
        return [pile for pile in self.game.s['rows'] if len(pile.cards) > 1]

    @state.move_state('deal')
    def deal_cards(self, sound=False):
        """
        Method called by game talon deal

        Overide to move base card to top for each pile.

        deal_cards([sound:bool=False]) -> int
        """
        ncards = 0
        game = self.game
        if sound:
            game._play_sound('deal')
        for pile in self._piles:
            lpile = len(pile.cards)
            ncards += lpile
            game._move_do(a_move.AInnerMove(pile, from_pos=0, to_pos=lpile),
                          reset=True)  # low level
        game._move_do(a_move.ANextRoundMove(self), reset=True)  # low level
        return ncards


class Draw1_SS_RowStack(stack.SS_RowStack):

    def move_pile(self, ncards, to_stack, **kw):
        """
        Overide for build on s[reserves] to drop or play next card and replace.
        Else call next method in order.

        .. move_pile(ncards:int, to_stack:Pile[, *animation keywords*]) -> None
        """
        game, reserve = self.game, self.game.s['reserves'][0]
        if to_stack is not reserve:
            super(Draw1_SS_RowStack, self).move_pile(ncards, to_stack, **kw)
            return
        from_stack = self._drawCards()
        assert from_stack and reserve.caps['base_rank'] == cs.ANY and ncards == 1
        # 1) top card from self to reserve
        game._move_do(a_move.AUpdateStackMove(reserve, c.UNDO))  # low level
        game._move_do(a_move.AMoveMove(ncards, self, reserve, **kw),
                      reset=True)  # low level
        game._move_do(a_move.AUpdateStackMove(reserve, c.REDO))  # low level
        # 2) second card from self to foundation or row
        if game.can_play_sound:
            game._play_sound('drop', priority=200)
        game._move_do(a_move.AMoveMove(ncards, self, from_stack, **kw),
                      reset=True)  # low level
        # 3) from reserve back to self
        self._fill_cards(**kw)

    def _draw_cards(self):
        """
        Return pile next card builds with or None.

        .. \_draw_cards() -> Pile
        """
        if len(self.cards) >= 2:
            to_pile = self.cards[-2:-1]
            for from_pile in self.game.s['foundations'] + self.game.s['rows']:
                if from_pile is not self and from_pile.cardsBuild(self, to_pile):
                    return from_pile

    @state.move_state('fill')
    def _fill_cards(self, **kw):
        """
        Fill pile with next card from s[reserves].

        .. \_draw_cards() -> Pile
        """
        self.game.s['reserves'][0].move_pile(1, self, **kw)  # low level

class CloverLeaf_RowStack(stack.UD_SS_RowStack):

    def cardsBuild(self, from_stack, cards):
        """
        cardsBuild(from_stack:obj, cards:tup) -> bool

        Check if base card is ACE or KING, then if parent can build with cards
        from from_stack.

        :param object from_stack: pile playing cards.
        :param tuple cards: cards playing on pile.
        :return: pile can build with cards from played from_stack?
        :rtype: boolean

        seealso:
          Method pysollib.stackcaps.builds for the *capability* check.
        """
        if not self.cards and not cards[0]['rank'] in (cs.ACE, cs.KING):
            return False
        return super(CloverLeaf_RowStack, self).cardsBuild(from_stack, cards)

    def _getBaseCard(self):
        return _('Base card - Ace or King.')


class ShadeRK_RowStack(stack.UD_SS_RowStack):
    _BOTTOM_IMAGE = 'shade'


class Draw1Reserve(stack.FromRowStackBuildMethod, stack.ReserveStack):

    def cardsBuild(self, from_stack, cards):
        """
        cardsBuild(from_stack:obj, cards:tup) -> bool

        Check if parent can build with cards
        from from_stack then if from_stack._drawCards. After build, set
        caps.base_rank GAP to prevent further draws.

        :param object from_stack: pile playing cards.
        :param tuple cards: cards playing on pile.
        :return: pile can build with cards from played from_stack?
        :rtype: boolean

        seealso:
          Method pysollib.stackcaps.builds for the *capability* check.
        """
        if (not super(Draw1Reserve, self).cardsBuild(from_stack, cards) and
            not from_stack._drawCards()):
            return False
        self.game._save_stack_caps(self, base_rank=cs.GAP)
        return True

    @state.not_state('is_preview')
    def _update_text(self):
        if not self.text['misc']:
            return
        t = (_('X'), _('Draw'))[self.caps['base_rank'] is cs.GAP]
        self.texts['misc'].config(text=t)


class Fan(game.Game):
    """
    Finish cards on _foundation within a turn.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.freecell.FanLayout
        - row [18 over 5 col S foundation, horz offset]
        - talon [1 SE, no offset]
        - foundation [4 horz NE, no offset]

    _row_
      Pile 18 over 5 cols _foundation_. Init deal [3, 3, 3, 3, 3, 3, 3, 3, 3,
      3, 3, 3, 3, 3, 3, 3, 3, 2] open card. Build down in rank by same suit.
      Play top. Base KING.

    _foundation_
      Pile 4 N. Build up in rank by same suit. Play top.

    note::
      ⟪highlight_play⟫ disabled
    ------
    :cvar Stack _talon_: stack.InitialDealTalon
    :cvar Stack _foundation_: stack.SS_Foundation,
    :cvar Stack _row_: stack.SS_RowStack (base_rank=KING, max_move=1,
      max_accept=1)
    :cvar Stack _reserve_: None
    :cvar Hint _hint_: hint.CautiousDefaultHint
    """
    __info__ = dict(id=56, name='Fan', game_type=(GT.FAN_TYPE | GT.OPEN),
                    decks=1, redeals=0, skill_level=SL.MOSTLY_SKILL,
                    version='3.00')
    __author__ = 'Markus Oberhumer'
    __version__ = 7.70

    _layout_ = FreeCellLayout.FanLayout
    _talon_ = stack.InitialDealTalon
    _foundations_ = (stack.SS_Foundation,)
    _reserve_ = None
    _row_ = Wrap(stack.SS_RowStack, base_rank=cs.KING, max_move=1,
                         max_accept=1)

    def _create_game(self, **kw):
        kwdefault(kw, rows=18, ncols=5, playcards=9)
        return super(Fan, self)._create_game(**kw)

    def _start(self, rows=3, flip=True, **kw):
        for i in range(rows - 1):
            self.s['talon'].dealRow(flip=flip, frames=0)
        self._play_sound('deal')
        self.s['talon'].dealRow(row=self.s['rows'][:-1])
        if flip == False:
            self.flip(self.s['rows'][-1])

    @property
    def _highlight_piles(self):
        """
        Get stacks to highlight for possible move.

        :return: empty.
        :rtype: tuple
        """
        return ()


class ScotchPatience(Fan):
    """
    Finish cards on _foundation within a turn.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.freecell.FanLayout
        - row [18 over 5 col S foundation, horz offset]
        - talon [1 SE, no offset]
        - foundation [4 horz NE, no offset]

    _row_
      Init deal [3, 3, 3, 3, 3, 3, 3, 3, 3,
      3, 3, 3, 3, 3, 3, 3, 3, 2] open card. Build down in rank alone.
      Play top. Base none.

    _foundation_
      Build up in rank by alt color suit. Play top.

    note::
      ⟪highlight_play⟫ disabled
    ------
    :cvar Stack _talon_: stack.InitialDealTalon
    :cvar Stack _foundation_: stack.AC_Foundation,
    :cvar Stack _row_: stack.RK_RowStack (base_rank=GAP, max_move=1,
      max_accept=1)
    :cvar Stack _reserve_: None
    :cvar Hint _hint_: hint.CautiousDefaultHint
    """
    __info__ = dict(id=87, name='Scotch Patience', game_type=(GT.FAN_TYPE |
                    GT.OPEN), decks=1, redeals=0, skill_level=SL.MOSTLY_SKILL,
                    version='3.00')
    __author__ = 'Markus Oberhumer'
    __version__ = 7.70

    _foundations_ = (stack.AC_Foundation,)
    _row_ = Wrap(stack.RK_RowStack, base_rank=cs.GAP, max_move=1, max_accept=1)

    def _create_game(self, **kw):
        kw.setdefault('playcards', 8)
        return super(ScotchPatience, self)._create_game(**kw)


class Shamrocks(Fan):
    """
    Finish cards on _foundation within a turn.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.freecell.FanLayout
        - row [18 over 5 col S foundation, horz offset]
        - talon [1 SE, no offset]
        - foundation [4 horz NE, no offset]

    _row_
      Init deal [3, 3, 3, 3, 3, 3, 3, 3, 3,
      3, 3, 3, 3, 3, 3, 3, 3, 2] open card. Build either in rank alone.
      Play top. Base none. Max 3 cards.

    _foundation_
      Build up in rank by same suit. Play top.

    note::
      ⟪highlight_play⟫ disabled
    ------
    :cvar Stack _talon_: stack.InitialDealTalon
    :cvar Stack _foundation_: stack.SS_Foundation,
    :cvar Stack _row_: stack.UD_RK_RowStack (base_rank=gap, max_cards=3)
    :cvar Stack _reserve_: None
    :cvar Hint _hint_: hint.CautiousDefaultHint
    """
    __info__ = dict(id=57, name='Shamrocks', game_type=(GT.FAN_TYPE |
                    GT.OPEN), decks=1, redeals=0, version='3.00',
                    skill_level=SL.MOSTLY_SKILL)
    __author__ = 'Markus Oberhumer'
    __version__ = 7.70

    _row_ = Wrap(stack.UD_RK_RowStack, base_rank=cs.GAP, max_cards=3)

    def _create_game(self, **kw):
        kw.setdefault('playcards', 4)
        return super(Shamrocks, self)._create_game(**kw)


class ShamrocksII(Shamrocks):
    """
    Finish cards on _foundation within a turn.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.freecell.FanLayout
        - row [18 over 5 col S foundation, horz offset]
        - talon [1 SE, no offset]
        - foundation [4 horz NE, no offset]

    _row_
      Init deal [3, 3, 3, 3, 3, 3, 3, 3, 3,
      3, 3, 3, 3, 3, 3, 3, 3, 2] open card with base KING. Build either in
      rank alone. Play top. Base none. Max 3 cards.

    _foundation_
      Build up in rank by same suit. Play top.

    note::
      ⟪highlight_play⟫ disabled

    seealso::
      http://en.wikipedia.org/wiki/Shamrocks
    ------
    :cvar Stack _talon_: stack.InitialDealTalon
    :cvar Stack _foundation_: stack.SS_Foundation,
    :cvar Stack _row_: stack.UD_RK_RowStack (base_rank=gap, max_cards=3)
    :cvar Stack _reserve_: None
    :cvar Hint _hint_: hint.CautiousDefaultHint
    """
    __info__ = dict(id=714, name='Shamrocks Ⅱ', game_type=(GT.FAN_TYPE |
                    GT.OPEN), decks=1, redeals=0, skill_level=SL.MOSTLY_SKILL,
                    version='6.10')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    def _pack_place(self, cards):
        """
        _pack_place(cards:seq(Card ...)) -> seq(Card ...)

        Overide to return KING as base of _row_ deal.
        """
        return place.placeBaseCard(cards, len(self.s['rows']), rank=cs.KING)


class LaBelleLucie(Fan):
    """
    Finish cards on _foundation within 3 turn.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.freecell.FanLayout
        - row [18 over 5 col S foundation, horz offset]
        - talon [1 SE, no offset]
        - foundation [4 horz NE, no offset]

    _row_
      Init deal [3, 3, 3, 3, 3, 3, 3, 3, 3,
      3, 3, 3, 3, 3, 3, 3, 3, 2] open card. Build down in rank by same suit.
      Play top. Base GAP.

    _talon_
      Redeal _row_ with 3 cards each.

    _foundation_
      Pile 4 N. Build up in rank by same suit. Play top.

    note::
      ⟪highlight_play⟫ disabled
    ------
    :cvar Stack _talon_: ShuffleRedealTalon (num_deal=3, max_rounds=3)
    :cvar Stack _foundation_: stack.SS_Foundation,
    :cvar Stack _row_: stack.SS_RowStack (base_rank=GAP, max_move=1,
      max_accept=1)
    :cvar Stack _reserve_: None
    :cvar Hint _hint_: hint.CautiousDefaultHint
    """
    __info__ = dict(id=901, name='La Belle Lucie', game_type=(GT.FAN_TYPE |
                    GT.OPEN | GT.XPAT), decks=1, redeals=2,
                    skill_level=SL.MOSTLY_SKILL, altnames=('Fair Lucy',
                    'Midnight Oil',), version='2.90')
    __author__ = 'Markus Oberhumer'
    __version__ = 7.70

    _talon_ = Wrap(ShuffleRedealTalon, num_deal=3, max_rounds=3)
    _row_ = Wrap(stack.SS_RowStack, base_rank=cs.GAP, max_move=1, max_accept=1)

    def talon_redeal(self, sound=True):
        """
        talon_redeal(self, sound=True):

        Redeal from talon after 1st round after cards returned to refill _row_
        with 4 cards until empty. Returns num cards dealt.
        """
        talon, rows = self.s['talon'], self.s['rows']
        ndeal = talon.num_deal
        n = divmod(min(len(talon.cards), len(rows) * ndeal), ndeal)
        lrows = n[0] + (1 if n[1] else 0)
        for i in range(ndeal - 1):
            self.dealRowAvail(rows=rows[:lrows], frames=4)
        self.dealRowAvail(frames=4)


class SuperFlowerGarden(LaBelleLucie):
    """
    Finish cards on _foundation within 3 turn.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.freecell.FanLayout
        - row [18 over 5 col S foundation, horz offset]
        - talon [1 SE, no offset]
        - foundation [4 horz NE, no offset]

    _row_
      Init deal [3, 3, 3, 3, 3, 3, 3, 3, 3,
      3, 3, 3, 3, 3, 3, 3, 3, 2] open card. Build down in rank alone. Play
      top. Base GAP.

    _talon_
      Redeal _row_ with 3 cards each.

    _foundation_
      Build up in rank by same suit. Play top.

    note::
      ⟪highlight_play⟫ disabled
    ------
    :cvar Stack _talon_: ShuffleRedealTalon (num_deal=3, max_rounds=3)
    :cvar Stack _foundation_: stack.SS_Foundation,
    :cvar Stack _row_: stack.RK_RowStack (base_rank=GAP, max_move=1,
      max_accept=1)
    :cvar Stack _reserve_: None
    :cvar Hint _hint_: hint.CautiousDefaultHint
    """
    __info__ = dict(id=132, name='Super Flower Garden', game_type=(GT.FAN_TYPE
                    | GT.OPEN), decks=1, redeals=2, version='3.20',
                    skill_level=SL.MOSTLY_SKILL)
    __author__ = 'Markus Oberhumer'
    __version__ = 7.70

    _row_ = Wrap(stack.RK_RowStack, base_rank=cs.GAP, max_move=1, max_accept=1)


class ThreeShufflesAndADraw(LaBelleLucie):
    """
    Finish cards on _foundation within 3 turn.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.freecell.FanLayout
        - row [18 over 5 col S foundation, horz offset]
        - talon [1 SE, no offset]
        - reserve [1 W, talon, no offset]
        - foundation [4 horz NE, no offset]

    _row_
      Init deal [3, 3, 3, 3, 3, 3, 3, 3, 3,
      3, 3, 3, 3, 3, 3, 3, 3, 2] open card. Build down in rank by same suit.
      Play top. Play on _reserve_ once when next finish on _foundation_ or
      play _row_, then top is returned. Base GAP.

    _reserve_
      Pile 1 E _talon_. Build any 1 card once. Play none.

    _talon_
      Pile 1 SW. Redeal _row_ with 3 cards each.

    _foundation_
      Pile 4 N. Build up in rank by same suit. Play top.

    note::
      ⟪highlight_play⟫ disabled
    ------
    :cvar Stack _talon_: ShuffleRedealTalon (num_deal=3, max_rounds=3)
    :cvar Stack _foundation_: stack.SS_Foundation,
    :cvar Stack _row_: Draw1_SS_RowStack (base_rank=GAP, max_move=1,
      max_accept=1)
    :cvar Stack _reserve_: Draw1Reserve
    :cvar Hint _hint_: hint.CautiousDefaultHint
    """
    __info__ = dict(id=128, name='Three Shuffles and a Draw',
                    game_type=(GT.FAN_TYPE | GT.OPEN), decks=1, redeals=2,
                    skill_level=SL.MOSTLY_SKILL, version='3.20')
    __author__ = 'Markus Oberhumer'
    __version__ = 7.70

    _row_ = Wrap(Draw1_SS_RowStack, base_rank=cs.GAP, max_move=1, max_accept=1)
    _reserve_ = Draw1Reserve

    def _create_game(self, **kw):
        l, s = super(ThreeShufflesAndADraw, self)._create_game(**kw), self.s
        x, y = s['talon'].x - l.XS, s['talon'].y
        s['reserves'].append(self._reserve_(x, y, game=self))
        l.set_misc_text(s['reserves'], anchor='center')
        l.defaultStackGroups()

    def _start(self, **kw):
        self._update_text()
        super(ThreeShufflesAndADraw, self)._start(**kw)


class Trefoil(LaBelleLucie):
    """
    Finish cards on _foundation within 3 turn.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.freecell.FanLayout
        - row [16 over 5 col S foundation, horz offset]
        - talon [1 SE, no offset]
        - foundation [4 horz NE, no offset]

    _row_
      Init deal 3 open card. Build down in
      rank by same suit. Play top. Base GAP.

    _talon_
      Redeal _row_ with 3 cards each.

    _foundation_
      Init deal base ACE. Build up in rank by same suit. Play top.

    note::
      ⟪highlight_play⟫ disabled
    ------
    :cvar Stack _talon_: ShuffleRedealTalon (num_deal=3, max_rounds=3)
    :cvar Stack _foundation_: stack.SS_Foundation (min_cards=1)
    :cvar Stack _row_: stack.SS_RowStack (base_rank=GAP, max_move=1,
      max_accept=1)
    :cvar Stack _reserve_: None
    :cvar Hint _hint_: hint.CautiousDefaultHint
    """
    __info__ = dict(id=88, name='Trefoil', game_type=(GT.FAN_TYPE | GT.OPEN),
                    decks=1, redeals=2, skill_level=SL.MOSTLY_SKILL,
                    version='3.00')
    __author__ = 'Markus Oberhumer'
    __version__ = 7.70

    _foundations_ = (Wrap(stack.SS_Foundation, min_cards=1),)

    def _create_game(self, **kw):
        kw.setdefault('rows', 16)
        return super(Trefoil, self)._create_game(**kw)

    def _pack_place(self, cards):
        """
        _pack_place(cards:seq(Card ...)) -> seq(Card ...)

        Overide to return ACE on bottom by suit, deck for _foundation_ deal.
        """
        return place.MoveToBottom(cards, place.AllAce)

    def _start(self, rows=3, flip=True, **kw):
        for i in range(rows - 1):
            self.s['talon'].dealRow(frames=0, flip=True)
        self._play_sound('deal')
        self.s['talon'].dealRow()
        self.s['talon'].dealRow(rows=self.s['foundations'])


class Troika(Fan):
    """
    Finish cards on _foundation within a turn.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.freecell.FanLayout
        - row [18 over 6 col S foundation, horz offset]
        - talon [1 SE, no offset]
        - foundation [4 horz NE, no offset]

    _row_
      Init deal 3 open card or finish ACE.
      Build same in rank. Play top. Base none.

    _foundation_
      Build up in rank by same suit. Play top.

    note::
      ⟪highlight_play⟫ disabled
    ------
    :cvar Stack _talon_: stack.InitialDealTalon
    :cvar Stack _foundation_: stack.SS_Foundation,
    :cvar Stack _row_: ShadeRK_RowStack (dir=0, base_rank=GAP, max_cards=3,
      max_move=1, max_accept=1)
    :cvar Stack _reserve_: None
    :cvar Hint _hint_: hint.CautiousDefaultHint
    """
    __info__ = dict(id=516, name='Troika', game_type=(GT.FAN_TYPE | GT.OPEN),
                    decks=1, redeals=0, skill_level=SL.MOSTLY_SKILL,
                    version='0.93fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _row_ = Wrap(ShadeRK_RowStack, dir=0, base_rank=cs.GAP, max_cards=3,
                         max_move=1, max_accept=1)

    def _create_game(self, **kw):
        kwdefault(kw, ncols=6, playcards=4)
        return super(Troika, self)._create_game(**kw)

    def _start(self, rows=3):
        for i in range(rows - 1):
            self.s['talon'].deal_row_or_drop(rows=self.s['rows'], frames=0)
        self._play_sound('deal')
        self.s['talon'].deal_row_or_drop(rows=self.s['rows'])


class Quads(Troika):
    """
    Finish cards on _foundation within a turn.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.freecell.FanLayout
        - row [13 over 5 col S foundation, horz offset]
        - talon [1 SE, no offset]
        - foundation [4 horz NE, no offset]

    _row_
      Init deal 4 open card or finish ACE.
      Build same in rank. Play top. Base none.

    _foundation_
      Build up in rank by same suit. Play top.

    note::
      ⟪highlight_play⟫ disabled
    ------
    :cvar Stack _talon_: stack.InitialDealTalon
    :cvar Stack _foundation_: stack.SS_Foundation,
    :cvar Stack _row_: ShadeRK_RowStack (dir=0, base_rank=GAP, max_cards=4)
    :cvar Stack _reserve_: None
    :cvar Hint _hint_: hint.CautiousDefaultHint
    """
    __info__ = dict(id=517, name='Quads', game_type=(GT.FAN_TYPE | GT.OPEN |
                    GT.ORIGINAL), decks=1, redeals=0, version='0.93fc',
                    skill_level=SL.MOSTLY_SKILL)
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _row_ = Wrap(ShadeRK_RowStack, dir=0, base_rank=cs.GAP, max_cards=4)

    def _create_game(self, **kw):
        kwdefault(kw, rows=13, playcards=5)
        return super(Quads, self)._create_game(**kw)

    def _start(self, **kw):
        kw.setdefault('rows', 4)
        super(Quads, self)._start(**kw)


class QuadsPlus(Quads):
    """
    Finish cards on _foundation within a turn.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.freecell.FanLayout
        - row [13 over 5 col S foundation, horz offset]
        - talon [1 SE, no offset]
        - foundation [4 horz NE, no offset]

    _row_
      Init deal [3, 3, 3, 3, 3, 3, 3, 3, 3,
      3, 0] open card. Build same in rank. Play top. Base none.

    _foundation_
      Init deal base ACE. Build up in rank by same suit. Play top.

    note::
      ⟪highlight_play⟫ disabled
    ------
    :cvar Stack _talon_: stack.InitialDealTalon
    :cvar Stack _foundation_: stack.SS_Foundation,
    :cvar Stack _row_: ShadeRK_RowStack (dir=0, base_rank=GAP, max_cards=4)
    :cvar Stack _reserve_: None
    :cvar Hint _hint_: hint.CautiousDefaultHint
    """
    __info__ = dict(id=767, name='Quads +', game_type=(GT.FAN_TYPE |
                    GT.OPEN | GT.ORIGINAL), decks=1, redeals=0,
                    skill_level=SL.MOSTLY_SKILL, version='0.93fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    def _pack_place(self, cards):
        """
        _pack_place(cards:seq(Card ...)) -> seq(Card ...)

        Overide to return ACE on top by suit, deck for _foundation_ deal.
        """
        return place.MoveToTop(cards, place.AllAce)

    def _start(self, rows=3, **kw):
        self.s['talon'].dealRow(rows=self.s['foundations'], frames=0)
        for i in range(rows - 1):
            self.s['talon'].dealRow(rows=self.s['rows'][:-1], frames=0)
        self._play_sound('deal')
        self.s['talon'].dealRow(rows=self.s['rows'][:-1])


class Intelligence(LaBelleLucie):
    """
    Finish cards on _foundation within 3 turn.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.freecell.FanLayout
        - row [18 over 5 col S foundation, horz offset]
        - talon [1 SE, no offset]
        - foundation [8 horz NE, no offset]

    _row_
      Init deal 3 open card or finish with
      replace. Build either in rank by same suit. Play top. Base GAP. Gap play
      or finish with replace 1 card from _talon_.

    _talon_
      Redeal _row_ with 3 cards each.

    _foundation_
      Build up in rank by same suit. Play top.

    note::
      ⟪highlight_play⟫ disabled

    seealso::
      http://en.wikipedia.org/wiki/Intelligence_%28solitaire%29
    ------
    :cvar Stack _talon_: ShuffleRedealTalon (num_deal=3, max_rounds=3)
    :cvar Stack _foundation_: stack.SS_Foundation,
      stack.SS_Foundation,
    :cvar Stack _row_: stack.UD_SS_RowStack (base_rank=GAP)
    :cvar Stack _reserve_: None
    :cvar Hint _hint_: hint.CautiousDefaultHint
    """
    __info__ = dict(id=227, name='Intelligence', game_type=GT.FAN_TYPE,
                    decks=2, redeals=2, skill_level=SL.BALANCED,
                    version='4.60')
    __author__ = 'Markus Oberhumer'
    __version__ = 7.70

    _foundation_ = (stack.SS_Foundation,) * 2
    _talon_ = Wrap(ShuffleRedealTalon, num_deal=3, max_rounds=3)
    _row_ = Wrap(stack.UD_SS_RowStack, base_rank=cs.GAP)

    def _create_game(self, **kw):
        kw.setdefault('texts', True)
        return super(Intelligence, self)._create_game(**kw)

    def _start(self, rows=3, **kw):
        """
        Deal initial cards for new game.
          - s[rows]: 3 ea face up drop w/ replace.

        .. \_start([rows=3]) -> None
        """
        talon = self.s['talon']
        for i in range(rows - 1):
            talon.deal_row_and_drop(ncards=1, frames=0)
        self._play_sound('deal')
        talon.deal_row_and_drop(ncards=1)

    @state.move_state('deal')
    def _auto_fill(self, stack):
        fill.fromTalonFinish(self, stack, self.s['rows'])


class IntelligencePlus(Intelligence):
    """
    Finish cards on _foundation within 3 turn.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.freecell.FanLayout
        - row [19 over 5 col S foundation, horz offset]
        - talon [1 SE, no offset]
        - foundation [8 horz NE, no offset]

    _row_
      Init deal 3 open card or finish with
      replace. Build either in rank by same suit. Play top. Base GAP. Gap play
      or finish with replace 1 card from _talon_.

    _talon_
      Redeal _row_ with 3 cards each.

    _foundation_
      Build up in rank by same suit. Play top.

    note::
      ⟪highlight_play⟫ disabled
    ------
    :cvar Stack _talon_: ShuffleFinishRedealTalon (num_deal=3, max_rounds=3)
    :cvar Stack _foundation_: stack.SS_Foundation,
      stack.SS_Foundation,
    :cvar Stack _row_: stack.UD_SS_RowStack (base_rank=GAP, max_move=1,
      max_accept=1)
    :cvar Stack _reserve_: None
    :cvar Hint _hint_: hint.CautiousDefaultHint
    """
    __info__ = dict(id=340, name='Intelligence +', game_type=GT.FAN_TYPE,
                    decks=2, redeals=2, skill_level=SL.BALANCED,
                    version='0.90fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    def _create_game(self, **kw):
        kw.setdefault('rows', 19)
        return super(IntelligencePlus, self)._create_game(**kw)


class HouseInTheWood(Fan):
    """
    Finish cards on _foundation within a turn.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.freecell.FanLayout
        - row [35 over 6 col S foundation, horz offset]
        - talon [1 SE, no offset]
        - foundation [8 horz NE, no offset]

    _row_
      Init deal [3, 3, 3, 3, 3, 3, 3, 3, 3,
      3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
      3, 2] open card. Build either in rank by same suit. Play top. Base GAP.

    _foundation_
      Build up in rank by same suit. Play top.

    note::
      ⟪highlight_play⟫ disabled

    seealso::
      http://en.wikipedia.org/wiki/House_in_the_Woods
    ------
    :cvar Stack _talon_: stack.InitialDealTalon
    :cvar Stack _foundation_: stack.SS_Foundation,
      stack.SS_Foundation,
    :cvar Stack _row_: stack.UD_SS_RowStack (base_rank=GAP)
    :cvar Stack _reserve_: None
    :cvar Hint _hint_: hint.CautiousDefaultHint
    """
    __info__ = dict(id=268, name='House in the Woods', game_type=(GT.FAN_TYPE
                    | GT.OPEN), decks=2, redeals=0, version='0.80fc',
                    skill_level=SL.MOSTLY_SKILL)
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _foundation_ = (stack.SS_Foundation,) * 2
    _row_ = Wrap(stack.UD_SS_RowStack, base_rank=cs.GAP)

    def _create_game(self, **kw):
        kwdefault(kw, rows=35, ncols=6)
        return super(HouseInTheWood, self)._create_game(**kw)


class HouseOnTheHill(HouseInTheWood):
    """
    Finish cards on _foundation within a turn.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.freecell.FanLayout
        - row [35 over 6 col S foundation, horz offset]
        - talon [1 SE, no offset]
        - foundation [8 horz NE, no offset]

    _row_
      Init deal [3, 3, 3, 3, 3, 3, 3, 3, 3,
      3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
      3, 2] open card. Build either in rank by same suit. Play top. Base GAP.

    _foundation_
      Build in rank by same suit. E down. W up. Play top.

    note::
      ⟪highlight_play⟫ disabled

    seealso::
      http://en.wikipedia.org/wiki/House_in_the_Woods
    ------
    :cvar Stack _talon_: stack.InitialDealTalon
    :cvar Stack _foundation_: stack.SS_Foundation,
      stack.SS_Foundation,
      stack.SS_Foundation (base_rank=KING, dir=-1)
    :cvar Stack _row_: stack.UD_SS_RowStack (base_rank=GAP)
    :cvar Stack _reserve_: None
    :cvar Hint _hint_: hint.CautiousDefaultHint
    """
    __info__ = dict(id=317, name='House on the Hill', game_type=(GT.FAN_TYPE
                    | GT.OPEN), decks=2, redeals=0, version='0.80fc',
                    skill_level=SL.MOSTLY_SKILL)
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _foundations_ = (stack.SS_Foundation,
                     Wrap(stack.SS_Foundation, base_rank=cs.KING, dir=-1),)


class CloverLeaf(Fan):
    """
    Finish cards on _foundation within a turn.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.gypsy.GypsyLayout
        - row [16 over 4 col NW, vert offset]
        - talon [1 SE, no offset]
        - foundation [4 vert E, no offset]

    _row_
      Init deal 3 open card. Build either in
      rank by same suit. Play top. Base GAP.

    _foundation_
      Init deal base ACE clubs hearts and KING spades, diamonds.
      Build in rank by same suit. E down. W up. Play top.

    note::
      ⟪highlight_play⟫ disabled
    ------
    :cvar Stack _talon_: stack.InitialDealTalon
    :cvar Stack _foundation_: stack.SS_Foundation,
      stack.SS_Foundation (base_rank=KING, dir=-1)
    :cvar Stack _row_: CloverLeaf_RowStack (base_rank=GAP)
    :cvar Stack _reserve_: None
    :cvar Hint _hint_: hint.CautiousDefaultHint
    """
    __info__ = dict(id=320, name='Clover Leaf', game_type=(GT.FAN_TYPE |
                    GT.OPEN), decks=1, redeals=0, version='0.80fc',
                    skill_level=SL.MOSTLY_SKILL)
    __author__ = 'Skomoroh'
    __credits__ = ['Thomas Warfield']
    __version__ = 7.70

    _layout_ = GypsyLayout.GypsyLayout
    _foundations_ = (stack.SS_Foundation,
                     Wrap(stack.SS_Foundation, base_rank=cs.KING,
                                  dir=-1),)
    _row_ = Wrap(CloverLeaf_RowStack, max_move=1, max_accept=1)

    def _create_game(self, **kw):
        kwdefault(kw, rows=16, ncols=4, playcards=0)
        return super(CloverLeaf, self)._create_game(**kw)

    def _start(self, rows=3, flip=True, **kw):
        for i in range(rows - 1):
            self.s['talon'].dealRow(flip=flip, frames=0)
        self._play_sound('deal')
        self.s['talon'].dealRow()
        self.s['talon'].dealRow(rows=self.s['foundations'])

    def _pack_place(self, cards):
        """
        _pack_place(cards:seq(Card ...)) -> seq(Card ...)

        Overide to return ACE club heart and KING spade diamonds on bottom by
        rank, suit, deck for _foundation_ deal.
        """
        return postion.MoveToBottom(cards,
                   lambda c: (place.move(c, ranks=[cs.ACE], suits=[0, 1]) or
                              place.move(c, ranks=[cs.KING], suits=[2, 3])))


class FreeFan(Fan):
    """
    Finish cards on _foundation within a turn.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.freecell.FanLayout
        - row [18 over 5 col S foundation, horz offset]
        - talon [1 SE, no offset]
        - foundation [4 horz NE, no offset]
        - reserve [2 horz NW, no offset]

    _row_
      Init deal [3, 3, 3, 3, 3, 3, 3, 3, 3,
      3, 3, 3, 3, 3, 3, 3, 3, 2] open card. Build down in rank by same suit.
      Play top or or seq if possible as super move. Base KING.

    _reserve_
      Build any 1 card. Play top.

    _foundation_
      Build up in rank by same suit. Play top.

    note::
      ⟪highlight_play⟫ disabled
    ------
    :cvar Stack _talon_: stack.InitialDealTalon
    :cvar Stack _foundation_: stack.SS_Foundation,
    :cvar Stack _row_: stack.SS_RowStack (base_rank=KING, max_move=1,
      max_accept=1)
    :cvar Stack _reserve_: None
    :cvar Hint _hint_: hint.CautiousDefaultHint
    """
    __info__ = dict(id=347, name='Free Fan', game_type=(GT.FAN_TYPE |
                    GT.OPEN), decks=1, redeals=0, version='0.90fc',
                    skill_level=SL.MOSTLY_SKILL)
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _row_ = Wrap(stack.SuperMoveSS_RowStack, base_rank=cs.KING)

    def _create_game(self, **kw):
        kwdefault(kw, reserves=2, playcards=8)
        return super(FreeFan, self)._create_game(**kw)


class FascinationFan(LaBelleLucie):
    """
    Finish cards on _foundation within 7 turn.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.freecell.FanLayout
        - row [18 over 5 col S foundation, horz offset]
        - talon [1 SE, no offset]
        - foundation [4 horz NE, no offset]

    _row_
      Init deal [3, 3, 3, 3, 3, 3, 3, 3, 3,
      3, 3, 3, 3, 3, 3, 3, 3, 2] top 1 open card. Build down in rank by
      alt color. Play top. Base KING.

    _talon_
      Redeal _row_ with 3 top 1 open cards each.

    _foundation_
      Build up in rank by same suit. Play top.

    note::
      ⟪highlight_play⟫ disabled
    ------
    :cvar Stack _talon_: ShuffleRedealTalon, num_deal=3, max_rounds=7)
    :cvar Stack _foundation_: stack.SS_Foundation,
    :cvar Stack _row_: stack.AC_RowStack (base_rank=GAP, max_move=1,
      max_accept=1)
    :cvar Stack _reserve_: None
    :cvar Hint _hint_: hint.CautiousDefaultHint
    """
    __info__ = dict(id=625, name='Fascination Fan', game_type=GT.FAN_TYPE,
                    decks=1, redeals=6, skill_level=SL.BALANCED,
                    altnames=('Demon Fan',), version='0.93fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = Wrap(ShuffleRedealTalon, num_deal=3, max_rounds=7)
    _row_ = Wrap(stack.AC_RowStack, base_rank=cs.GAP, max_move=1, max_accept=1)

    def _start(self, **kw):
        kw.setdefault('flip', False)
        super(FascinationFan, self)._start(**kw)

    def talon_redeal(self, sound=True):
        """
        talon_redeal(self, sound=True):

        Redeal from talon after 1st round after cards returned to refill _row_
        with 4 cards until empty. Returns num cards dealt.
        """
        talon, rows = self.s['talon'], self.s['rows']
        ndeal = talon.num_deal
        n = divmod(min(len(talon.cards), len(rows) * ndeal), ndeal)
        lrows = n[0] + (1 if n[1] else 0)
        for i in range(ndeal - 1):
            self.dealRowAvail(rows=rows[:lrows], flip=False, frames=4)
        self.dealRowAvail(frames=4)


class BoxFan(Fan):
    """
    Finish cards on _foundation within a turn.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.freecell.FanLayout
        - row [16 over 4 col S foundation, horz offset]
        - talon [1 SE, no offset]
        - foundation [4 horz NE, no offset]

    _row_
      Init deal 3 open card. Build down in
      rank by alt color suit. Play top. Base KING.

    _foundation_
     Init deal base ACE. Build up in rank by same suit. Play top.

    note::
      ⟪highlight_play⟫ disabled
    ------
    :cvar Stack _talon_: stack.InitialDealTalon
    :cvar Stack _foundation_: stack.SS_Foundation,
    :cvar Stack _row_: stack.AC_RowStack (base_rank=KING, max_move=1,
      max_accept=1)
    :cvar Stack _reserve_: None
    :cvar Hint _hint_: hint.CautiousDefaultHint
    """
    __info__ = dict(id=385, name='Box Fan', game_type=(GT.FAN_TYPE | GT.OPEN),
                    decks=1, redeals=0, skill_level=SL.MOSTLY_SKILL,
                    version='0.90fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _row_ = Wrap(stack.AC_RowStack, base_rank=cs.KING, max_move=1,
                         max_accept=1)

    def _create_game(self, **kw):
        kwdefault(kw, rows=16, ncols=4)
        return super(BoxFan, self)._create_game(**kw)

    def _pack_place(self, cards):
        """
        _pack_place(cards:seq(Card ...)) -> seq(Card ...)

        Overide to return ACE on top by suit, deck for _foundation_ deal.
        """
        return place.MoveToTop(cards, place.AllAce)

    def _start(self, rows=3, **kw):
        self.s['talon'].dealRow(rows=self.s['foundations'])
        for i in range(rows - 1):
            self.s['talon'].dealRow(frames=0)
        self._play_sound('deal')
        self.s['talon'].dealRow()


class School(BoxFan):
    """
    Finish cards on _foundation within 3 turn.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.freecell.FanLayout
        - row [16 over 4 col S foundation, horz offset]
        - talon [1 SE, no offset]
        - foundation [4 horz NE, no offset]

    _row_
      Init deal 3 open card. Build same in
      rank alone. Play top. Base none.

    _foundation_
      Build up in rank by same suit. Play top.

    note::
      ⟪highlight_play⟫ disabled
    ------
    :cvar Stack _talon_: ShuffleRedealTalon (max_rounds=3)
    :cvar Stack _foundation_: stack.SS_Foundation,
    :cvar Stack _row_: stack.RK_RowStack (base_rank=GAP, dir=0, max_move=1,
      max_accept=1)
    :cvar Stack _reserve_: None
    :cvar Hint _hint_: hint.CautiousDefaultHint
    """
    __info__ = dict(id=719, name='School', game_type=GT.FAN_TYPE, decks=1,
                    redeals=2, skill_level=SL.MOSTLY_SKILL, version='1.10fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = Wrap(ShuffleRedealTalon, max_rounds=3)
    _row_ = Wrap(stack.RK_RowStack, dir=0, base_rank=cs.GAP, max_move=1,
                         max_accept=1)

    def _create_game(self, **kw):
        kw.setdefault('playcards', 10)
        return super(School, self)._create_game(**kw)


class Crescent(BoxFan):
    """
    Finish cards on _foundation within 3 turn.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.klondike.CrescentLayout
        - row [16 over 4 row S foundation, horz offset]
        - talon [1 NE, no offset]
        - foundation [8 horz N, no offset]

    _row_
      Init deal 5 open card. Build either in
      rank by same suit wrap. Play top. Base none.

    _talon_
      Move _row_ base to top.

    _foundation_
      Init deal base ACE KING. Build in rank by same suit. E up.
      W down. Play top.

    note::
      ⟪highlight_play⟫ disabled
    ------
    :cvar Stack _talon_: Crescent_Talon (max_rounds=4)
    :cvar Stack _foundation_: stack.SS_Foundation,
      stack.SS_Foundation (base_rank=KING, dir=-1)
    :cvar Stack _row_: stack.UD_SS_RowStack (base_rank=GAP, mod=13)
    :cvar Stack _reserve_: None
    :cvar Hint _hint_: hint.CautiousDefaultHint
    """
    __info__ = dict(id=647, name='Crescent', game_type=GT.FAN_TYPE, decks=2,
                    redeals=3, skill_level=SL.MOSTLY_SKILL, version='0.93fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _layout_ = KlondikeLayout.CrescentLayout
    _talon_ = Wrap(Crescent_Talon, max_rounds=4)
    _foundations_ = (stack.SS_Foundation,
                     Wrap(stack.SS_Foundation, base_rank=cs.KING, dir=-1),)
    _row_ = Wrap(stack.UD_SS_RowStack, base_rank=cs.GAP, mod=13)

    def _create_game(self, **kw):
        l, s = super(Crescent, self)._create_game(**kw), self.s
        for i in range(2):
            s['internals'].append(stack.InvisibleStack(self))
        return l

    def _pack_place(self, cards):
        """
        _pack_place(cards:seq(Card ...)) -> seq(Card ...)

        Overide to return ACE and KING of suit on top by rank, deck for
        _foundation_ deal.
        """
        return place.MoveToTop(cards, place.OneAceKing)

    def _start(self, **kw):
        kw.setdefault('rows', 5)
        super(Crescent, self)._start(**kw)


class ForestGlade(game.Game):
    """
    Finish cards on _foundation within 3 turn.

    _row_
      Pile 16 over 3 cols _foundation_. Init deal 3 open card. Build down in
      rank by same suit. Play top. Base KING.

    _talon_
      Pile 1 NE. Deal 3 card on _row_. Gap redeal _row_

    _foundation_
      Pile 8 4 E 4 W. Build up in rank by same suit. Play top.

    ------
    :cvar Stack _talon_: stack.DealRowRedealTalon (num_deal=3,
      max_rounds=3)
    :cvar Stack _foundation_: stack.SS_Foundation (dir=2, max_cards=7),
      stack.SS_Foundation (base_rank=TWO, dir=2, max_cards=6)
    :cvar Stack _row_: stack.SS_RowStack (base_rank=KING, max_move=1)
    :cvar Hint _hint_: hint.CautiousDefaultHint
    """
    __info__ = dict(id=739, name='Forest Glade', game_type=GT.FAN_TYPE,
                    decks=2, redeals=2, skill_level=SL.MOSTLY_SKILL,
                    version='1.10fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = Wrap(stack.DealRowRedealTalon, num_deal=3, max_rounds=3)
    _foundations_ = (Wrap(stack.SS_Foundation, dir=2, max_cards=7),
                     Wrap(stack.SS_Foundation, base_rank=cs.TWO, dir=2,
                          max_cards=6),)
    _row_ = Wrap(stack.SS_RowStack, max_move=1, base_rank=cs.KING)
    _hint_ = CautiousDefaultHint

    def _create_game(self, rows=16, ncols=3, playcards=7, **kw):
        l, s = layout.Layout(self), self.s
        outer = ncols - 1
        rows -= outer * 2
        rows = l._get_row_size(rows, ncols)
        rows = (outer,) + rows + (outer,)
        dx = l._get_stack_width(playcards - 1)
        self.table_size = (l.XM + max(rows) * dx + l.NDECKS * l.XS,
                           l.YM + (len(rows) + 1) * l.YS,)
        right = lambda p: self.width - p * l.XS
        center = lambda p: (self.width - p * dx) / 2
        for ddx, _foundation_ in zip((l.XM, right(2),), self._foundations_):
            for y, suit in zip(l.col_iter(l.YM, l.NSUIT), l.SUIT):
                for x in l.row_iter(ddx, l.NDECKS):
                    s['foundations'].append(_foundation_(x, y, suit=suit))
        for y, row in zip(l.col_iter(l.YM, len(rows)), rows):
            for x in l.row_iter(center(row), row, dx):
                s['rows'].append(self._row_(x, y, game=self,
                                         offset=(l.XOFFSET, 0)))
        x, y = l.XM, self.height - l.YS
        s['talon'] = self._talon_(x, y)
        l.set_ncards_text(s['talon'], 'ne')
        l.set_rounds_text(s['talon'], 'se')
        l.defaultStackGroups()
        return l

    def _start(self, rows=3, flip=True, **kw):
        for i in range(rows - 1):
            self.s['talon'].dealRow(flip=flip, frames=0)
        self._play_sound('deal')
        self.s['talon'].dealRow()


class BuffaloBill(game.Game):
    """
    Finish cards on _foundation within 1 turn.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.klondike.CrescentLayout
        - row [26 over 7 row S foundation, horz offset]
        - talon [1 NW, no offset]
        - reserve [8 horz S, no offset]
        - foundation [8 horz N, no offset]

    _row_
      Init deal 4 open card. Build none.
      Play top.

    _reserve_
      Build any 1 card. Play top.

    _foundation_
      Build in rank by same suit. Up W, Down E. Play top.
    ------
    :cvar Stack _talon_: stack.InitialDealTalon
    :cvar Stack _foundation_: stack.SS_Foundation, stack.SS_Foundation
      (base_rank=KING, dir=-1)
    :cvar Stack _row_: stack.BasicRowStack
    :cvar Stack _reserve_: stack.ReserveStack
    :cvar Hint _hint_: hint.DefaultHint
    """
    __info__ = dict(id=338, name="Buffalo Bill", game_type=GT.FAN_TYPE,
                    decks=2, redeals=0, skill_level=SL.MOSTLY_SKILL,
                    version='0.90fc')
    __author__ = 'Skomoroh'
    __credits__ = ['David Parlett']
    __version__ = 7.70

    _layout_ = KlondikeLayout.CrescentLayout
    _talon_ = stack.InitialDealTalon
    _foundations_ = (stack.SS_Foundation,
                     Wrap(stack.SS_Foundation, base_rank=cs.KING, dir=-1),)
    _row_ = stack.BasicRowStack
    _reserve_ = stack.ReserveStack

    def _create_game(self, **kw):
        kwdefault(kw, rows=26, ncols=7, playcards=4, reserves=8)
        return super(BuffaloBill, self)._create_game(**kw)

    def _start(self, rows=4, flip=True, **kw):
        for i in range(rows - 1):
            self.s['talon'].dealRow(flip=flip, frames=0)
        self._play_sound('deal')
        self.s['talon'].dealRow()


class LittleBillie(BuffaloBill):
    """
    Finish cards on _foundation within 1 turn.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.klondike.CrescentLayout
        - row [24 over 6 row S foundation, horz offset]
        - talon [1 NW, no offset]
        - reserve [8 horz S, no offset]
        - foundation [8 horz N, no offset]

    _row_
      Init deal 4 open card. Build none.
      Play top.

    _reserve_
      Init deal 1 card. Build any 1 card. Play top.

    _foundation_
      Build in rank by same suit. Up W, Down E. Play top.
    ------
    :cvar Stack _talon_: stack.InitialDealTalon
    :cvar Stack _foundation_: stack.SS_Foundation, stack.SS_Foundation
      (base_rank=KING, dir=-1)
    :cvar Stack _row_: stack.BasicRowStack
    :cvar Stack _reserve_: stack.ReserveStack
    :cvar Hint _hint_: hint.DefaultHint
    """
    __info__ = dict(id=421, name="Little Billie", game_type=GT.FAN_TYPE,
                    decks=2, redeals=0, skill_level=SL.MOSTLY_SKILL,
                    version='0.92fc')

    def _create_game(self, **kw):
        kwdefault(kw, rows=24, ncols=6)
        return super(LittleBillie, self)._create_game(**kw)

    def _start(self):
        self.s['talon'].dealRow(rows=self.s['reserves'], frames=0)
        super(LittleBillie, self)._start()


PROVIDES = [Fan, ScotchPatience, Shamrocks, LaBelleLucie, SuperFlowerGarden,
            ThreeShufflesAndADraw, Trefoil, Intelligence, IntelligencePlus,
            HouseInTheWood, HouseOnTheHill, CloverLeaf, FreeFan, BoxFan,
            Troika, Quads, FascinationFan, Crescent, ShamrocksII, School,
            ForestGlade, QuadsPlus, BuffaloBill, LittleBillie]